今天開始比較完整一點的跟大家介紹線性模型。
什麼是線性模型呢?之前的Curve Fitting就是一種,還記得嗎?這個我們叫他basis function(基函數),他與 w 這個向量內積得到我們要的輸出,的定義有許多種,有線性的、也有非線性的,但是他們在這邊都是線性模型,為什麼呢?因為我們要去調整的是 w ,而 w 僅是與phi做內積,所以不管phi再怎麼樣非線性,他都是 w 的線性函數,這也是為什麼叫做線性模型。
今天我們先介紹一個相當實用的計算方式,也就是今天的標題之一循序學習。
為什麼要循序呢?在第一次的動手做看看,我們知道,也就是我們要進行矩陣的運算,而且不只是相乘,還要取反矩陣,取反矩陣其實是一個很麻煩的計算,當資料量太大的時候,這個計算會便得不可行,所以我們必須要循序學習。
所以我們要怎麼樣循序學習呢?我們利用梯度下降!
我們計算反矩陣的方式,是直接令他的一次微分等於零,而一次微分就是我們所謂的梯度。現在我們不直接令他等於零,我們透過迭代,一次讀一筆資料,一筆資料一次迭代,讓梯度接近最小值,迭代方式如下
其中那個很像n,可是右邊比較長的叫做eta,這邊是學習率(learning rate)的意思,我們需要調整他以確保w最後會收斂;而那個三角形又有一個w的意思是,對取w的梯度,也就是對w偏微分。
而這個就是最簡單的梯度下降法(gradient descent)!
但是這樣的方式,其實沒有辦法解決我們提到的問題,因為在計算E(w)的偏微分時,我還是得計算所有的資料,這是因為,裡面的累加,使得我們其實沒有減少到計算量。
因此我們會使用隨機梯度下降法(stochastic gradient descent)。
怎麼個隨機法呢?其實很簡單,就是我們直接不算那個累加,我們直接對這次拿到的資料進行微分就好了,如此一來我們的計算量就會少很多了!雖然需要花更久的時間才能使 w 迭代到我們要的值,可是會使得我們的計算便得可行。也就是我們的更新方式成為了
下面是用這個方式算出來的曲線,藍色是以之前算反矩陣的方式算,橘色則是以SGD的方式算
下面是畫出上圖的程式碼,關於eta的選擇,其實有很多理論可以參考,大家有興趣也可以去查閱。不過我這邊就是很簡單的try & error選出來一個會收斂的數字!
def phi(x, degree):
X = [1.]
out = float(x)
for term in range(degree):
X += [float(out)]
out = out * float(x)
X = np.asarray(X)
return X
def regression(target, feature, degree, lamda):
target = np.asarray(target)
mat = phi(feature[0], degree)
for i in range(1, len(feature)):
mat = np.vstack((mat, phi(feature[i], degree)))
if lamda == 0:
inverse = pinv(mat)
else:
inverse = inv(mat.T.dot(mat) + lamda*np.identity(degree + 1)).dot(mat.T)
return inverse.dot(target)
#stochastic gradient descent
def sgd(target, feature, order):
w = np.zeros((order+1, 1))
eta = 0.0001 #eta需要小心選擇確保收斂
for epoch in range(30000): #看完所有資料一次算一代(epoch)
for n in range(len(target)):
w += eta*(np.asarray(target[n]).reshape(1) - w.T.dot(phi(feature[n],order).reshape(order+1,1)))*phi(feature[n],order).reshape(order+1,1) #循序更新
return w
def f(x):
return np.sin(x) + np.random.normal(0,1,len(x))
train_data = np.linspace(0,6,50)
train_target = f(train_data)
order = 2 #用幾次方的phi去fit,改變order也需要更新你的eta與epoch確保他收斂
plt.plot(train_data, train_target, 'bo')
w = regression(train_target, train_data, order, 0)
ws = sgd(train_target, train_data, order)
print ws
print w
line = np.linspace(0,6,50)
lx = []
sx = []
for l in line:
lx += [w.dot(phi(l, order))]
sx += [ws.T.dot(phi(l, order).reshape((order+1,1))).reshape(1)]
plt.plot(line, lx, linestyle ='-')
plt.plot(line, sx)
plt.show()
從這個例子可以看到,在這種scale的問題中,以SGD是沒效率的,不過正如前面所說,若資料很多時,也只能使用這樣的方式了,另外就是當沒有公式解的時候,這就會是一個很好逼近最佳解的方式了!